﻿
using LibNiconico.NiconicoApi.Exceptions;
using LibNiconico.NiconicoApi.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using XmlSockets;


namespace LibNiconico.NiconicoApi.Internal
{
    /// <summary>
    /// ニコニコXmlSocketコメントクライアント
    /// </summary>
    internal abstract class NicoXmlSocketCommentClient : NicoXmlCommentClient,IDisposable
    {
        /// <summary>
        /// メッセージ送信用アクション
        /// </summary>
        /// <param name="message"></param>
        internal protected delegate void SendAction(XElement message);

        /// <summary>
        /// XmlSocketクライアント
        /// </summary>
        protected ClientXmlSocket xmlSocket;

        /// <summary>
        /// 接続情報
        /// </summary>
        internal protected NicoXmlSocketCommentConnectionInfo XmlSocketConnectionInfo
        {
            get
            {
                return base.XmlConnectionInfo as NicoXmlSocketCommentConnectionInfo;
            }

            set
            {
                base.XmlConnectionInfo = value;
            }

        }

        

        /// <summary>
        /// 接続済みか判定
        /// </summary>
        public bool Connected
        {
            get
            {
                return this.xmlSocket.Connected;
            }
        }

        /// <summary>
        /// 非同期にコメント送信
        /// </summary>
        /// <param name="chatComment"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        internal protected async Task SendAsync( NicoXmlSocketSubmitChatComment chatComment, SendAction func)
        {
            XElement message =  CreateSubmitChatComment(chatComment);
            
            func(message);

            try
            {
                await xmlSocket.SendAsync(message).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                new NicoApiException("チャットコメント送信中にエラーが発生しました", e);
            }

        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="auth"></param>
        /// <param name="createInfo"></param>
        /// <param name="synchronizationContext"></param>
        internal NicoXmlSocketCommentClient(NiconicoAuth auth, NiconicoApiInfoGetCreateInfo createInfo, SynchronizationContext synchronizationContext)
            : base(auth, createInfo, synchronizationContext)
        {
            xmlSocket = new ClientXmlSocket(new XmlSocketCreateInfo(),synchronizationContext);
            xmlSocket.Closed += onClose;
            xmlSocket.Received += received;

        }

        private void received(ClientXmlSocketReceiveEventArgs args)
        {
            try
            {
                OnReadChat(args.Messages);
            }
            catch (Exception e)
            {
                OnProtectedReadException(e);
            }

        }



        private void onClose(ClientXmlSocketCloseEventArgs args)
        {
            OnProtectedClose(args.Reason, args.Exception);
        }

        /// <summary>
        /// 非同期に接続
        /// </summary>
        /// <param name="connectionInfo"></param>
        /// <param name="extendInfo"></param>
        /// <returns></returns>
        internal protected async Task ConnectAsync(NicoXmlSocketCommentConnectionInfo connectionInfo,NicoXmlSocketCommentConnectionExtendInfo extendInfo)
        {
            try
            {
                await xmlSocket.ConnectAsync(connectionInfo.Host, connectionInfo.Port).ConfigureAwait(false);
                await SendReceiveRequest(connectionInfo, new NicoXmlSocketReceiveRequest
                {
                    ResFrom = extendInfo.ResFrom,
                    Version = extendInfo.Version
                }).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                throw new NicoApiException(e.Message, e);
            }
        }

        /// <summary>
        /// 接続を切断
        /// </summary>
        public void Close()
        {
            xmlSocket.Close();
            
        }

        /// <summary>
        /// コメント受信要求送信
        /// </summary>
        /// <param name="connectionInfo"></param>
        /// <param name="req"></param>
        /// <returns></returns>
        private async Task SendReceiveRequest(NicoXmlSocketCommentConnectionInfo connectionInfo, NicoXmlSocketReceiveRequest req)
        {
            XElement element = CreateReceiveRequestMessage(connectionInfo, req);
            await xmlSocket.SendAsync(element).ConfigureAwait(false);
        }
        

        

        protected abstract void OnReadChat(IEnumerable<XElement> messages);

        /// <summary>
        /// このオブジェクトが保有しているリソースを解放
        /// </summary>
        public virtual void Dispose()
        {
            xmlSocket.Dispose();
        }
    }
}
